﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Xml.Linq;

namespace MvcEngine.Core.Base
{
    /// <summary>
    /// Base class for all entities in the framework.
    /// </summary>
    [Serializable]
    [DebuggerStepThrough]
    public abstract class Entity
    {
        private FieldsDictionary fields;

        /// <summary>
        /// Constructs new instance of <see cref="Entity"/>.
        /// </summary>
        public Entity()
        {
            fields = new FieldsDictionary();
        }

        public Entity(Entity entity)
            : this()
        {

            entity.Fields.CopyTo(this.fields);
            this.Xml = entity.Xml;
        }

        /// <summary>
        /// Gets the value of nullable variable.
        /// </summary>
        /// <typeparam name="T">The type of variable.</typeparam>
        /// <param name="nullableValue">The nullable variable which value needs to be get.</param>
        /// <returns>Actual value of <paramref name="nullableValue"/> if it is available; 
        /// if not, then the default value of type <typeparamref name="T"/> is returned.</returns>
        protected T GetValue<T>(T? nullableValue)
            where T : struct
        {
            if (nullableValue.HasValue)
                return nullableValue.Value;
            else
                return default(T);
        }

        #region IEntity Members

        /// <summary>
        /// Holds the dictionary of entity's custom fields.
        /// </summary>
        public FieldsDictionary Fields
        {
            get { return fields; }
            set { fields = value; }
        }

        /// <summary>
        /// Gets or sets a field value by its name.
        /// </summary>
        /// <param name="name">Name of the field.</param>
        /// <returns>The value of the field.</returns>
        public object this[string name]
        {
            get { return fields.GetValue(name); }
            set { fields.SetValue(name, value); }
        }

        ///// <summary>
        ///// Copies the properties marked with the <see cref="Sana.Core.Entities.XmlFieldAttribute"/> and custom fields from the <paramref name="other"/> entity.
        ///// Attribute should be set on the property of current entity to be copied.
        ///// </summary>
        ///// <param name="other">The other entity.</param>
        //public void CopyFieldsFrom(Entity other)
        //{
        //    if (other == null) return;
        //    var type = other.GetType();
        //    var props = this.GetType().GetProperties();
        //    foreach (var pi in props)
        //    {
        //        if (pi.GetCustomAttributes(typeof(XmlFieldAttribute), false).Length > 0)
        //        {
        //            var pi2 = type.GetProperty(pi.Name);
        //            if (pi2 != null && pi.PropertyType == pi2.PropertyType)
        //                pi.SetValue(this, pi2.GetValue(other, null), null);
        //        }
        //    }
        //    other.Fields.CopyTo(this.Fields);
        //}

        /// <summary>
        /// Gets the field value.
        /// </summary>
        /// <typeparam name="T">The type of the value.</typeparam>
        /// <param name="name">The name of the field.</param>
        /// <param name="defaultValue">The default value to be returned if the field does not exist.</param>
        /// <returns>
        /// The field value or a <paramref name="defaultValue"/> if the field does not exist.
        /// </returns>
        public T GetValue<T>(string name, T defaultValue)
        {
            object value = fields.GetValue(name);
            return value != null ? (T)value : defaultValue;
        }

        public abstract string Xml { get; set; }

        #endregion
    }
}
